#include "head.h"
#include <func.h>
int exitPipe[2];
void sigFunc(int signum){
    puts("signal is coming!");
    write(exitPipe[1], "1", 1);
}

int main(int argc, char *argv[]){
    // ./server 192.168.31.128 1234 3
    ARGS_CHECK(argc, 4);
    /*打开日志记录*/
    openlog(NULL,LOG_PID,LOG_USER);

    /*打开数据库*/
    MYSQL *db = mysql_init(NULL);
    mysqlConnet(db);

    /*初始化父子进程管道*/
    pipe(exitPipe);
    if(fork() != 0){
        close(exitPipe[0]);//close read pipe
        signal(SIGUSR1, sigFunc);
        wait(NULL);
        exit(0);
    }
    close(exitPipe[1]);//close write pipe

    /*得到一个用于产生token的盐值*/
    char saltForToken[50] = {0};
    getSalt(saltForToken);
    printf("saltForToken = %s\n", saltForToken);

    /*初始化线程池结构体+开启线程池*/
    int workerNum = atoi(argv[3]);
    threadPool_t threadPool;
    threadPoolInit(&threadPool, workerNum);
    int sockFd;
    makeWorker(&threadPool);
    tcpInit(argv[1], argv[2], &sockFd);

    /*epoll监听*/
    int epFd = epoll_create(10);
    epollAdd(sockFd, epFd);
    epollAdd(exitPipe[0], epFd);
    struct epoll_event readyList[2];

    while(1){
        int readyNum = epoll_wait(epFd, readyList, 2, -1);
        puts("epoll ready");
        for(int i=0; i<readyNum; ++i){
            if(readyList[i].data.fd == sockFd){
                int netFd = accept(sockFd, NULL, NULL);

                /*res:
                    1:register; 2:login*/
                char res;
                recv(netFd, &res, 1, MSG_WAITALL);

                train_t train;
                /*接收用户名*/
                int dataLength;
                recv(netFd, &dataLength, sizeof(dataLength), MSG_WAITALL);
                char userName[100] = {0};
                recv(netFd, userName, dataLength, MSG_WAITALL);

                char token[100] = {0};
                if(res == '1'){//注册
                    /*
                        函数内
                            1.收密码
                            2.将token发送回客户端，保存在文件中
                    */
                    userRegister(db, userName, saltForToken, token, netFd);
                }else if(res == '2'){//登录
                    /* 登录的三种情况：
                        1.客户端本地设备保存token信息
                        2.客户端本地设备保存token信息但token已过期
                        3.客户端本地设备未保存token信息*/

                    char loginInfo;
                    recv(netFd, &loginInfo, sizeof(loginInfo), MSG_WAITALL);
                    printf("loginInfo = %c\n", loginInfo);
                    if(loginInfo == '0'){
                        //情况1、2

                        //收到token
                        recv(netFd, &dataLength, sizeof(dataLength), MSG_WAITALL);
                        recv(netFd, token, dataLength, MSG_WAITALL);
                        // printf("token = %s\n", token);
                        int ret = isRightToken(saltForToken, userName, token);
                        // printf("isRightToken = %d\n", ret);
                        if(ret == 0){
                            //token匹配，身份验证成功
                            puts("legal user");
                            //send isRightToken
                            send(netFd, "1", 1, MSG_NOSIGNAL);
                        }else{
                            //token不匹配，情况1、2
                            //需要验证用户密码是否正确

                            //send isRightToken
                            send(netFd, "0", 1, MSG_NOSIGNAL);
                            
                            if(isLegalUser(db, userName, netFd) == 0){
                                //send isLegalUser
                                puts("legal User");
                                send(netFd, "1", 1, MSG_NOSIGNAL);
                                //合法用户
                                //将token发送给客户端保存在本地
                                bzero(token, sizeof(token));
                                makeToken(saltForToken, userName, token);

                                //发送token
                                bzero(&train, sizeof(train_t));
                                strcpy(train.buf, token);
                                train.dataLength = strlen(train.buf);
                                send(netFd, &train, sizeof(train.dataLength)+train.dataLength, MSG_NOSIGNAL);
                            }else{
                                send(netFd, "0", 1, MSG_NOSIGNAL);
                                //非法用户，关掉客户端
                                close(netFd);
                                continue;
                            }
                        }
                    }else{
                        //3.客户端本地设备未保存token信息
                        //验证用户身份
                        if(isLegalUser(db, userName, netFd) == 0){
                            //合法用户
                            send(netFd, "1", 1, MSG_NOSIGNAL);
                            //将新token发送给客户端保存在本地
                            bzero(token, sizeof(token));
                            makeToken(saltForToken, userName, token);

                            //发送token
                            bzero(&train, sizeof(train_t));
                            strcpy(train.buf, token);
                            train.dataLength = strlen(train.buf);
                            send(netFd, &train, sizeof(train.dataLength)+train.dataLength, MSG_NOSIGNAL);
                        }else{
                            send(netFd, "0", 1, MSG_NOSIGNAL);
                        }
                    }
                }else{
                    close(netFd);
                    continue;
                }
                

                /*记录用户上线操作*/
                time_t ttime;
                time(&ttime);
                struct tm *pTm = gmtime(&ttime);
                syslog(LOG_INFO, "%s %04d:%02d:%02d", userName, (1900 + pTm->tm_year), (1 + pTm->tm_mon), (pTm->tm_mday));

                

                pthread_mutex_lock(&threadPool.mutex);
                enUserQueue(&threadPool.userQueue, netFd, userName);
                puts("new user");
                pthread_cond_signal(&threadPool.cond);
                pthread_mutex_unlock(&threadPool.mutex);
            }
            
            if(readyList[i].data.fd == exitPipe[0]){
                // for(int j=0; j<workerNum; j++){
                //     pthread_cancel(threadPool.tidArr[j]);
                // }
                puts("I am going to die!");
                threadPool.isThreadPoolRunning = 0;
                pthread_cond_broadcast(&threadPool.cond);
                for(int j=0; j<workerNum; ++j){
                    pthread_join(threadPool.tidArr[j], NULL);
                }
                puts("exit");
                exit(0);
            }
        }
    }
    closelog();
    mysql_close(db);
}